xlv.obj.liveGrid={
	extend:function(){
		this.extend('xlv.obj.table');
	},
	implement:function(){
	},
	create:function(tag){
		var div=document.createElement((tag||'div'));
		div.setAttribute('xlvobject','xlv.obj.liveGrid');
		Element.addClassName(div,'class_liveGrid');
		return div;					 
	},
	properties:{
		dmName:null,
		columns:null, //[{title:'',dmPath:'',viewer:''}]
		editable:true,
		headHeight:28,
		showHeader:true,
		rowHeight:0,
		cellWidth:0
	},
	method:{
		construct:function(){
			Element.addClassName(this,'class_liveGrid');
			if(this.$P.defaultStyle)Element.addClassName(this,'default_style');
			var v=this.$V;
			
			v.yscroller=document.createElement('div');
			v.yscroller.setAttribute('id','xlvid_livegrid_yscroller');
			if(Prototype.Browser.IE) v.yscroller.style.overflowY='scroll';
			v.heighter=document.createElement('div');
			v.heighter.setAttribute('id','xlvid_livegrid_heighter');
			v.yscroller.appendChild(v.heighter);
			this.appendChild(v.yscroller);
			
			v.viewport=xlv.util.findChild(this,{id:'xlvid_livegrid_viewport'});
			v.table=xlv.util.findChild(this,{tag:'table'});
			if(!v.viewport){
				v.viewport=document.createElement('div');
				v.viewport.setAttribute('id','xlvid_livegrid_viewport');
				this.appendChild(v.viewport);
				if(v.table)v.viewport.appendChild(v.table);
			}
			if(!v.table){
				v.table=document.createElement('table');
				v.table.setAttribute('cellpadding','0px');
				v.table.setAttribute('cellspacing','0px');
				v.viewport.appendChild(v.table);
				v.thead=document.createElement('thead');
				v.table.appendChild(v.thead);
				v.tbody=document.createElement('tbody');
				v.table.appendChild(v.tbody);
			}
			v.table.setAttribute('id','xlvid_livegrid_table');
			v.tbody=xlv.util.findDirectChild(v.table, {tag:'tbody'});
			if(!v.tbody){
				v.tbody=document.createElement('tbody');
				v.table.appendChild(v.tbody);
			}
			if(this.$P.dmName!=null && v.tbody){
				v._pattern=[];
				while(v.tbody.rows.length>0){
					v._pattern.push(v.tbody.rows[0]);
					v.tbody.rows[0].parentNode.removeChild(v.tbody.rows[0]);
				}
				for(var r=0;r<v._pattern.length;++r){
					var objs=xlv.util.descendants(v._pattern[r],{xlvobject:'*'});
					for(var i=0;i<objs.length;++i){
						objs[i]._$extended=false;
						objs[i]._$constructed=false;
						objs[i]._$exts=null;
						objs[i]._$imps=null;
					}
				}
				if(v._pattern.length==0)v._pattern=null;
			}
			if(!v.thead){
				v.thead=document.createElement('thead');
				v.table.insertBefore(v.thead,v.tbody);
			}
			v.table.setAttribute('id','xlvid_livegrid_table');
			v.thead.setAttribute('id','xlvid_livegrid_head');
			v.tbody.setAttribute('id','xlvid_livegrid_body');
			this.styleForId(v.table,v.table.id);
			this.styleForId(v.thead,v.thead.id);
			this.styleForId(v.tbody,v.tbody.id);
		},
		initial:function(){
			this.dnd_properties.dragOnly=true;
			this.dnd_properties.direction='horizontal';
			//
			this.addEventHandler('scroll', '_yscroll', this.$V.yscroller);
			this.addEventHandler('scroll', '_xscroll', this.$V.viewport);
			var name=(Prototype.Browser.IE)?'mousewheel':'DOMMouseScroll';
			this.addEventHandler(name, '_mousewheel', this.$V.viewport);
			this.addEventHandler('mousemove', '_head_mousemove',this.$V.thead);
			//
			this.$V.row_height=this.$P.rowHeight;
			this.$V.row_count=1;
			this.$V.row_visible=0;
			this.$V.rowStart=1;
			this.$V.last_scroll_top=0;
			this.$V.selected={row:0,cell:0};
			this.$V.idll_timeout=null;
			this.$V.skip_timeout=false;
			this.editable(this.$P.editable);
			this.selectable(this.$P.selectable);
		},
		showHeader:function(show){
			this.$V.thead.style.display=(show)?'':'none';
		},
		_mousewheel:function(event){
			Event.stop(event);
			var v=this.$V;
			v.skip_timeout=true;
			if(event.wheelDelta>=0||event.detail<0)
				 v.yscroller.scrollTop -=v.row_height;
			else v.yscroller.scrollTop +=v.row_height;
		},
		_xscroll:function(event){
		},
		_yscroll:function(event){
			var v=this.$V;
			if(v.idll_timeout){clearTimeout(v.idll_timeout);v.idll_timeout=null;}
			if(v.skip_timeout) this._scrollIdle();
			else v.idll_timeout=setTimeout((function(){this._scrollIdle();}).bind(this),10);
			v.skip_timeout=false;
		},
		_scrollIdle:function(){
			var v=this.$V;
			var rem=v.yscroller.scrollTop%v.row_height;
			if(rem != 0){
				v.skip_timeout=true;
				var top=v.yscroller.scrollTop;
				if(v.last_scroll_top>v.yscroller.scrollTop)top-=rem;
				else top+=(v.row_height-rem);
				if(top+v.row_height*v.row_count > v.yscroller.scrollHeight){
					var check=v.yscroller.scrollHeight-v.row_height*v.row_count-((Prototype.Browser.IE)? 1 :0);
					if(check>v.last_scroll_top) top=check;
				}
				v.yscroller.scrollTop=top;
				return;
			}
			v.last_scroll_top=v.yscroller.scrollTop;
			v.rowStart=v.yscroller.scrollTop/v.row_height+1;
			this._refreshRows();
		},
		_scrollToRow:function(row_no){
			var v=this.$V;
			v.skip_timeout=true;
			v.yscroller.scrollTop=(row_no-1)*v.row_height;
		},
		hasXScroller:function(){
			var t_dim=Element.getDimensions(this.$V.table);
			var c_dim=Element.getDimensions(this.$V.viewport);
			return (t_dim.width>c_dim.width);
		},
		_selectRowCell:function(row_no,cell_no){
			if(!row_no)return;
			var memo=xlv.sys.createMemoObject({src:this,from:{row:this.$V.selected.row,cell:this.$V.selected.cell}});
			var selected=this.$V.selected;
			var prev_tr=this.getRow(selected.row);
			if(prev_tr){
				Element.removeClassName(prev_tr,'selected');
				var last_cell=this.getCell(selected.row,selected.cell);
				if(last_cell)Element.removeClassName(last_cell,'selected');
			}
			selected.row=0;
			selected.cell=0;
			var row=this.getRow(row_no);
			if(!row){
				if(this.$V.rowStart+this.$V.row_count==row_no)this._scrollToRow(this.$V.rowStart+1);
				else this._scrollToRow(row_no);
			}else{
				if(row.offsetTop+row.offsetHeight>(this.$V.viewport.offsetHeight-(this.hasXScroller()? 17:0)))
					this._scrollToRow(this.$V.rowStart+1);
			}
			row=this.getRow(row_no);
			cell_no=(cell_no||selected.cell);
			if(row){
				Element.addClassName(row,'selected');
				var cell=this.getCell(row_no,cell_no);
				if(cell)Element.addClassName(cell,'selected');
			}
			selected.row=(row_no<0)? 0 :row_no;
			selected.cell=(cell_no<0)? 0 :cell_no;
			//
			memo.to={row:this.$V.selected.row,cell:this.$V.selected.cell};
			this.selected(memo);
			if(memo.from.row!=memo.to.row||memo.from.cell!=memo.to.cell){
				setTimeout((function(){Event.fire(this,'xlv:select_changed',memo);}).bind(this),1);
			}
		},
		//count from 1
		isRowVisible:function(row_no){
			var v=this.$V;
			return (row_no>=v.rowStart) &&(row_no<v.rowStart+v.row_visible);
		},
		_updateHeader:function(){
			var thead=this.$V.thead;
			if(thead.rows.length>0)return;
			var row=thead.insertRow(-1);
			var columns=this.$P.columns;
			for(var c=0;c<columns.length;++c){
				var cell=row.insertCell(-1);
				if(c==0&&columns[0].dmPath=='_rowNumber_') Element.addClassName(cell,'rowNumber');
				cell.setAttribute('unselectable','on');
				this.valueForHeader(c+1,cell);
				this.styleForHeader(c+1,cell);
			}
			var h_dim=Element.getDimensions(thead);
			var v_dim=Element.getDimensions(this.$V.viewport);
			var display=(h_dim.width>v_dim.width)?'':'none';
			var cell=row.insertCell(-1);
			cell.setAttribute('id','xlvid_head_patch');
			cell.style.width='17px';
			var w=17-cell.clientWidth+16;
			if(w>0)	cell.style.width=(17-cell.clientWidth+16)+'px';
			cell.style.dsplay=display;
		},
		_updateBody:function(){
			var v=this.$V;
			var tbody=this.$V.tbody;
			if(tbody.rows.length>0) return;
			//get row height
			var row=tbody.insertRow(-1);
			row.style.visibility='hidden';
			if(!row.cells[0])row.insertCell(-1);
			if(this.$P.rowHeight>0)row.cells[0].style.height=this.$P.rowHeight+'px';
			xlv.util.setElementValue(row.cells[0],'M');
			this.styleForCell(1,1,row.cells[0]);
			v.row_height=row.clientHeight;
			tbody.deleteRow(-1);
			var h_dim=Element.getDimensions(this.$V.thead);
			var v_dim=Element.getDimensions(this.$V.viewport);
			var height=v_dim.height-h_dim.height-((h_dim.width>v_dim.width)? 17:0);
			v.row_count=Math.ceil(height/v.row_height);
			for(var i=0;i<v.row_count;++i){
				var row=tbody.rows[i];
				if(!row){
					if(v._pattern) {
						tbody.appendChild(row=v._pattern[(i)%v._pattern.length].cloneNode(true));
						if(Prototype.Browser.IE)xlv.sys._patchIEProperties(row);
					}else row=tbody.insertRow(-1);
				}
				this.styleForRow(i+1,row);
				for(var c=0;c<this.$P.columns.length;++c){
					if(!row.cells[c])row.insertCell(-1);
					row.cells[c].style.height=v.row_height+'px';
					this.styleForCell(i+1,c+1,row.cells[c]);
				}
			}
		},
		_refreshRows:function(dm){
			dm=(dm||this.mvc_getModel());
			var v=this.$V;
			var dm_rows=dm.rows();
			var tbody=this.$V.tbody;
			v.row_visible=0;
			for(var i=0;i<v.row_count;++i){
				var row=tbody.rows[i];
				Element.removeClassName(row,'selected');
				Element.removeClassName(row,'removed');
				Element.removeClassName(row,'inserted');
				Element.removeClassName(row,'updated');
				Element.removeClassName(row,'odd');
				Element.removeClassName(row,'even');
				for(var c=0;c<this.$P.columns.length;++c)Element.removeClassName(row.cells[c],'selected');
				var rec_row=dm.getRow(v.rowStart+i);
				if(rec_row&&dm.isInsertedRow(rec_row))Element.addClassName(row,'inserted');
				if(rec_row&&dm.isRemovedRow(rec_row))Element.addClassName(row,'removed');
				if(rec_row&&dm.isUpdatedRow(rec_row))Element.addClassName(row,'updated');
				Element.addClassName(row,(((v.rowStart+i)%2)==0 ? 'even':'odd'));
				if(v.rowStart+i==v.selected.row){
					Element.addClassName(row,'selected');
					var sel=v.selected.cell;
					if(sel>0&&sel<=row.cells.length)Element.addClassName(row.cells[sel-1],'selected');
				}
				if(v.rowStart+i>dm_rows)Element.addClassName(row,'padding');
				else{
					++v.row_visible;
					Element.removeClassName(row,'padding');
					for(var c=0;c<this.$P.columns.length;++c){
						var cell=row.cells[c];
						if(c==0&&this.$P.columns[c].dmPath=='_rowNumber_') Element.addClassName(cell,'rowNumber');
						this.viewerForCell(dm,v.rowStart+i,c+1,cell);
						var value=(c==0&&this.$P.columns[0].dmPath=='_rowNumber_')?''+(v.rowStart+i):this.mvc_valueFromModel(dm,v.rowStart+i,c+1);
						this.mvc_valueToView(value,v.rowStart+i,c+1);
						this.styleForCell(v.rowStart+i,c+1,cell);
					}
				}
			}
			var style={top:this.$V.thead.offsetHeight+'px'};
			style.bottom=(v.viewport.scrollWidth<=v.viewport.clientWidth)?'0px':'17px';
			Element.setStyle(v.yscroller,style);
		},
		_resetScroller:function(dm){
			dm=(dm||this.mvc_getModel());
			var v=this.$V;
			var thead=this.$V.thead;
			var patch=thead.rows[0].cells[thead.rows[0].cells.length-1];
			var tbody=this.$V.tbody;
			var dm_rows=dm.rows();
			if(dm_rows<v.row_count){
				v.yscroller.scrollTop=0;
				Element.setStyle(v.heighter, {height:(v.row_height*dm_rows)+'px'});
				patch.style.display='none';
				if(Prototype.Browser.IE) v.yscroller.style.display='none';
				return;
			}
			patch.style.display='';
			if(Prototype.Browser.IE) v.yscroller.style.display='';
			var rows=(dm.isCompleted())?dm_rows+v.row_count-1:dm_rows;
			var h=v.row_height*rows;
			var scroll_top=(v.rowStart-1)*v.row_height;
			if(scroll_top+(v.row_count*v.row_height)>h) scroll_top=h-(v.row_count*v.row_height);
			scroll_top=(scroll_top<0)? 0 : scroll_top;
			if(Prototype.Browser.IE) h+=1;
			Element.setStyle(v.heighter, {height:h+'px'});
			v.yscroller.scrollTop=scroll_top;
		},
		/**mvc **/
		mvc_beforeModelChanged:function(dm,notify,memo){
		},
		mvc_modelChanged:function(dm,notify,memo){
			switch(notify){
				case 'nodeChanged':
					if(memo.type=='update'){
						var row_no=dm.rowNumber(memo.src.parentNode);
						if(this.isRowVisible(row_no)){
							for(var c=0;c<this.$P.columns.length;++c){
								var value=(c==0&&this.$P.columns[0].dmPath=='_rowNumber_')?''+row_no:
									  this.mvc_valueFromModel(dm,row_no,c+1);
								this.mvc_valueToView(value,row_no,c+1);
							}
							row=this.getRow(row_no);
							Element.addClassName(row,'updated');
						}
					}
					break;
			}
		},
		mvc_afterModelChanged:function(dm,notify,memo){
			if(!this.mvc_isModel(dm))return;
			switch(notify){
				case 'setChanged' :
					if(memo.type=='create'){
						this.$V.row_visible=0;
						this.$V.rowStart=1;
						this.$V.last_scroll_top=0;
						this.$V.selected={row:0,cell:0};
						this._makeColumns(dm);
						this._updateHeader();
						this._updateBody();
					}
					this._refreshRows(dm);
					this._resetScroller(dm);
					break;
				case 'rowChanged':
					if(memo.type== 'updated'){
						var row_no=dm.rowNumber(memo.src);
						if(this.isRowVisible(row_no)){
							row=this.getRow(row_no);
							Element.removeClassName(row,'updated');
						}
						break;
					}
					if(memo.type=='remove'||memo.type=='removed'){
						this._resetScroller(dm);this._refreshRows(dm);
					}
					if(memo.type== 'insert'||memo.type=='inserted'){
						this._resetScroller(dm);this._refreshRows(dm);
					}
					if(memo.type== 'cancel'){
						this._resetScroller(dm);this._refreshRows(dm);
					}
					break;
				case 'cursorMove' :
					var cell_no=(this.$V.cursorMove_cell_no||this.$V.selected.cell);
					//hack IE overflow 0
					this._selectRowCell(dm.rowNumber(memo.to),cell_no);
					break;
			}
			this.mvc_enableControl(dm);
		}
	}
};
